From 196c87d1574c5ce7dca4ff78990e3168a4dcad27 Mon Sep 17 00:00:00 2001 From: "kaf24@scramble.cl.cam.ac.uk" Date: Tue, 8 Feb 2005 11:07:10 +0000 Subject: [PATCH] bitkeeper revision 1.1159.212.126 (42089d5esMXb54hvuQX14wvXnCm18w) Various hypercall fixes for x86_64. Main todos: 1. mmu_updates/update_va_mapping hypercalls. 2. map perdomain_pt into Xen address space. 3. exception/interrupt callbacks to guest OS. 4. user-space ring 3 vs. guest-OS ring 3. Signed-off-by: keir.fraser@cl.cam.ac.uk --- xen/arch/x86/memory.c | 182 ++++++++++++++++++++++- xen/arch/x86/traps.c | 31 +++- xen/arch/x86/x86_32/mm.c | 178 +--------------------- xen/arch/x86/x86_64/entry.S | 6 +- xen/arch/x86/x86_64/mm.c | 252 +++----------------------------- xen/include/asm-x86/mm.h | 2 +- xen/include/asm-x86/multicall.h | 18 ++- 7 files changed, 252 insertions(+), 417 deletions(-) diff --git a/xen/arch/x86/memory.c b/xen/arch/x86/memory.c index e9fa4b370e..36a1463001 100644 --- a/xen/arch/x86/memory.c +++ b/xen/arch/x86/memory.c @@ -249,11 +249,13 @@ static inline void invalidate_shadow_ldt(struct exec_domain *d) static int alloc_segdesc_page(struct pfn_info *page) { - unsigned long *descs = map_domain_mem((page-frame_table) << PAGE_SHIFT); + struct desc_struct *descs; int i; + descs = map_domain_mem((page-frame_table) << PAGE_SHIFT); + for ( i = 0; i < 512; i++ ) - if ( unlikely(!check_descriptor(&descs[i*2])) ) + if ( unlikely(!check_descriptor(&descs[i])) ) goto fail; unmap_domain_mem(descs); @@ -1651,6 +1653,182 @@ int do_update_va_mapping_otherdomain(unsigned long page_nr, +/************************* + * Descriptor Tables + */ + +void destroy_gdt(struct exec_domain *ed) +{ + int i; + unsigned long pfn; + + for ( i = 0; i < 16; i++ ) + { + if ( (pfn = l1_pgentry_to_pagenr(ed->arch.perdomain_ptes[i])) != 0 ) + put_page_and_type(&frame_table[pfn]); + ed->arch.perdomain_ptes[i] = mk_l1_pgentry(0); + } +} + + +long set_gdt(struct exec_domain *ed, + unsigned long *frames, + unsigned int entries) +{ + struct domain *d = ed->domain; + /* NB. There are 512 8-byte entries per GDT page. */ + int i = 0, nr_pages = (entries + 511) / 512; + struct desc_struct *vgdt; + unsigned long pfn; + + /* Check the first page in the new GDT. */ + if ( (pfn = frames[0]) >= max_page ) + goto fail; + + /* The first page is special because Xen owns a range of entries in it. */ + if ( !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) + { + /* GDT checks failed: try zapping the Xen reserved entries. */ + if ( !get_page_and_type(&frame_table[pfn], d, PGT_writable_page) ) + goto fail; + vgdt = map_domain_mem(pfn << PAGE_SHIFT); + memset(vgdt + FIRST_RESERVED_GDT_ENTRY, 0, + NR_RESERVED_GDT_ENTRIES*8); + unmap_domain_mem(vgdt); + put_page_and_type(&frame_table[pfn]); + + /* Okay, we zapped the entries. Now try the GDT checks again. */ + if ( !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) + goto fail; + } + + /* Check the remaining pages in the new GDT. */ + for ( i = 1; i < nr_pages; i++ ) + if ( ((pfn = frames[i]) >= max_page) || + !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) + goto fail; + + /* Copy reserved GDT entries to the new GDT. */ + vgdt = map_domain_mem(frames[0] << PAGE_SHIFT); + memcpy(vgdt + FIRST_RESERVED_GDT_ENTRY, + gdt_table + FIRST_RESERVED_GDT_ENTRY, + NR_RESERVED_GDT_ENTRIES*8); + unmap_domain_mem(vgdt); + + /* Tear down the old GDT. */ + destroy_gdt(ed); + + /* Install the new GDT. */ + for ( i = 0; i < nr_pages; i++ ) + ed->arch.perdomain_ptes[i] = + mk_l1_pgentry((frames[i] << PAGE_SHIFT) | __PAGE_HYPERVISOR); + + SET_GDT_ADDRESS(ed, GDT_VIRT_START(ed)); + SET_GDT_ENTRIES(ed, entries); + + return 0; + + fail: + while ( i-- > 0 ) + put_page_and_type(&frame_table[frames[i]]); + return -EINVAL; +} + + +long do_set_gdt(unsigned long *frame_list, unsigned int entries) +{ + int nr_pages = (entries + 511) / 512; + unsigned long frames[16]; + long ret; + + if ( (entries <= LAST_RESERVED_GDT_ENTRY) || (entries > 8192) ) + return -EINVAL; + + if ( copy_from_user(frames, frame_list, nr_pages * sizeof(unsigned long)) ) + return -EFAULT; + + LOCK_BIGLOCK(current->domain); + + if ( (ret = set_gdt(current, frames, entries)) == 0 ) + { + local_flush_tlb(); + __asm__ __volatile__ ("lgdt %0" : "=m" (*current->arch.gdt)); + } + + UNLOCK_BIGLOCK(current->domain); + + return ret; +} + + +long do_update_descriptor( + unsigned long pa, unsigned long word1, unsigned long word2) +{ + unsigned long pfn = pa >> PAGE_SHIFT; + struct desc_struct *gdt_pent, d; + struct pfn_info *page; + struct exec_domain *ed; + long ret = -EINVAL; + + d.a = (u32)word1; + d.b = (u32)word2; + + LOCK_BIGLOCK(current->domain); + + if ( (pa & 7) || (pfn >= max_page) || !check_descriptor(&d) ) { + UNLOCK_BIGLOCK(current->domain); + return -EINVAL; + } + + page = &frame_table[pfn]; + if ( unlikely(!get_page(page, current->domain)) ) { + UNLOCK_BIGLOCK(current->domain); + return -EINVAL; + } + + /* Check if the given frame is in use in an unsafe context. */ + switch ( page->u.inuse.type_info & PGT_type_mask ) + { + case PGT_gdt_page: + /* Disallow updates of Xen-reserved descriptors in the current GDT. */ + for_each_exec_domain(current->domain, ed) { + if ( (l1_pgentry_to_pagenr(ed->arch.perdomain_ptes[0]) == pfn) && + (((pa&(PAGE_SIZE-1))>>3) >= FIRST_RESERVED_GDT_ENTRY) && + (((pa&(PAGE_SIZE-1))>>3) <= LAST_RESERVED_GDT_ENTRY) ) + goto out; + } + if ( unlikely(!get_page_type(page, PGT_gdt_page)) ) + goto out; + break; + case PGT_ldt_page: + if ( unlikely(!get_page_type(page, PGT_ldt_page)) ) + goto out; + break; + default: + if ( unlikely(!get_page_type(page, PGT_writable_page)) ) + goto out; + break; + } + + /* All is good so make the update. */ + gdt_pent = map_domain_mem(pa); + memcpy(gdt_pent, &d, 8); + unmap_domain_mem(gdt_pent); + + put_page_type(page); + + ret = 0; /* success */ + + out: + put_page(page); + + UNLOCK_BIGLOCK(current->domain); + + return ret; +} + + + /************************* * Writable Pagetables */ diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index 4cf22f86d7..301d7a765e 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -149,6 +149,11 @@ static inline int do_trap(int trapnr, char *str, if ( !GUEST_FAULT(regs) ) goto xen_fault; +#ifndef NDEBUG + if ( (ed->arch.traps[trapnr].address == 0) && (ed->domain->id == 0) ) + goto xen_fault; +#endif + ti = current->arch.traps + trapnr; tb->flags = TBF_EXCEPTION; tb->cs = ti->cs; @@ -314,6 +319,11 @@ asmlinkage int do_page_fault(struct xen_regs *regs) if ( !GUEST_FAULT(regs) ) goto xen_fault; +#ifndef NDEBUG + if ( (ed->arch.traps[TRAP_page_fault].address == 0) && (d->id == 0) ) + goto xen_fault; +#endif + propagate_page_fault(addr, regs->error_code); return 0; @@ -523,6 +533,12 @@ asmlinkage int do_general_protection(struct xen_regs *regs) return 0; #endif +#ifndef NDEBUG + if ( (ed->arch.traps[TRAP_gp_fault].address == 0) && + (ed->domain->id == 0) ) + goto gp_in_kernel; +#endif + /* Pass on GPF as is. */ ti = current->arch.traps + 13; tb->flags = TBF_EXCEPTION | TBF_EXCEPTION_ERRCODE; @@ -838,6 +854,13 @@ long do_fpu_taskswitch(void) } +#if defined(__i386__) +#define DB_VALID_ADDR(_a) \ + ((_a) <= (PAGE_OFFSET - 4)) +#elif defined(__x86_64__) +#define DB_VALID_ADDR(_a) \ + ((_a) >= HYPERVISOR_VIRT_END) || ((_a) <= (HYPERVISOR_VIRT_START-8)) +#endif long set_debugreg(struct exec_domain *p, int reg, unsigned long value) { int i; @@ -845,22 +868,22 @@ long set_debugreg(struct exec_domain *p, int reg, unsigned long value) switch ( reg ) { case 0: - if ( value > (PAGE_OFFSET-4) ) return -EPERM; + if ( !DB_VALID_ADDR(value) ) return -EPERM; if ( p == current ) __asm__ ( "mov %0, %%db0" : : "r" (value) ); break; case 1: - if ( value > (PAGE_OFFSET-4) ) return -EPERM; + if ( !DB_VALID_ADDR(value) ) return -EPERM; if ( p == current ) __asm__ ( "mov %0, %%db1" : : "r" (value) ); break; case 2: - if ( value > (PAGE_OFFSET-4) ) return -EPERM; + if ( !DB_VALID_ADDR(value) ) return -EPERM; if ( p == current ) __asm__ ( "mov %0, %%db2" : : "r" (value) ); break; case 3: - if ( value > (PAGE_OFFSET-4) ) return -EPERM; + if ( !DB_VALID_ADDR(value) ) return -EPERM; if ( p == current ) __asm__ ( "mov %0, %%db3" : : "r" (value) ); break; diff --git a/xen/arch/x86/x86_32/mm.c b/xen/arch/x86/x86_32/mm.c index 304f6d3188..d9d00cc3c6 100644 --- a/xen/arch/x86/x86_32/mm.c +++ b/xen/arch/x86/x86_32/mm.c @@ -212,9 +212,10 @@ long do_stack_switch(unsigned long ss, unsigned long esp) /* Returns TRUE if given descriptor is valid for GDT or LDT. */ -int check_descriptor(unsigned long *d) +int check_descriptor(struct desc_struct *d) { - unsigned long base, limit, a = d[0], b = d[1]; + unsigned long base, limit; + u32 a = d->a, b = d->b; /* A not-present descriptor will always fault, so is safe. */ if ( !(b & _SEGMENT_P) ) @@ -298,8 +299,8 @@ int check_descriptor(unsigned long *d) if ( !(b & _SEGMENT_G) ) goto bad; /* too dangerous; too hard to work out... */ limit = (limit >> 12) - 1; - d[0] &= ~0x0ffff; d[0] |= limit & 0x0ffff; - d[1] &= ~0xf0000; d[1] |= limit & 0xf0000; + d->a &= ~0x0ffff; d->a |= limit & 0x0ffff; + d->b &= ~0xf0000; d->b |= limit & 0xf0000; } } @@ -310,175 +311,6 @@ int check_descriptor(unsigned long *d) } -void destroy_gdt(struct exec_domain *ed) -{ - int i; - unsigned long pfn; - - for ( i = 0; i < 16; i++ ) - { - if ( (pfn = l1_pgentry_to_pagenr(ed->arch.perdomain_ptes[i])) != 0 ) - put_page_and_type(&frame_table[pfn]); - ed->arch.perdomain_ptes[i] = mk_l1_pgentry(0); - } -} - - -long set_gdt(struct exec_domain *ed, - unsigned long *frames, - unsigned int entries) -{ - struct domain *d = ed->domain; - /* NB. There are 512 8-byte entries per GDT page. */ - int i = 0, nr_pages = (entries + 511) / 512; - struct desc_struct *vgdt; - unsigned long pfn; - - /* Check the first page in the new GDT. */ - if ( (pfn = frames[0]) >= max_page ) - goto fail; - - /* The first page is special because Xen owns a range of entries in it. */ - if ( !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) - { - /* GDT checks failed: try zapping the Xen reserved entries. */ - if ( !get_page_and_type(&frame_table[pfn], d, PGT_writable_page) ) - goto fail; - vgdt = map_domain_mem(pfn << PAGE_SHIFT); - memset(vgdt + FIRST_RESERVED_GDT_ENTRY, 0, - NR_RESERVED_GDT_ENTRIES*8); - unmap_domain_mem(vgdt); - put_page_and_type(&frame_table[pfn]); - - /* Okay, we zapped the entries. Now try the GDT checks again. */ - if ( !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) - goto fail; - } - - /* Check the remaining pages in the new GDT. */ - for ( i = 1; i < nr_pages; i++ ) - if ( ((pfn = frames[i]) >= max_page) || - !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) - goto fail; - - /* Copy reserved GDT entries to the new GDT. */ - vgdt = map_domain_mem(frames[0] << PAGE_SHIFT); - memcpy(vgdt + FIRST_RESERVED_GDT_ENTRY, - gdt_table + FIRST_RESERVED_GDT_ENTRY, - NR_RESERVED_GDT_ENTRIES*8); - unmap_domain_mem(vgdt); - - /* Tear down the old GDT. */ - destroy_gdt(ed); - - /* Install the new GDT. */ - for ( i = 0; i < nr_pages; i++ ) - ed->arch.perdomain_ptes[i] = - mk_l1_pgentry((frames[i] << PAGE_SHIFT) | __PAGE_HYPERVISOR); - - SET_GDT_ADDRESS(ed, GDT_VIRT_START(ed)); - SET_GDT_ENTRIES(ed, entries); - - return 0; - - fail: - while ( i-- > 0 ) - put_page_and_type(&frame_table[frames[i]]); - return -EINVAL; -} - - -long do_set_gdt(unsigned long *frame_list, unsigned int entries) -{ - int nr_pages = (entries + 511) / 512; - unsigned long frames[16]; - long ret; - - if ( (entries <= LAST_RESERVED_GDT_ENTRY) || (entries > 8192) ) - return -EINVAL; - - if ( copy_from_user(frames, frame_list, nr_pages * sizeof(unsigned long)) ) - return -EFAULT; - - LOCK_BIGLOCK(current->domain); - - if ( (ret = set_gdt(current, frames, entries)) == 0 ) - { - local_flush_tlb(); - __asm__ __volatile__ ("lgdt %0" : "=m" (*current->arch.gdt)); - } - - UNLOCK_BIGLOCK(current->domain); - - return ret; -} - - -long do_update_descriptor( - unsigned long pa, unsigned long word1, unsigned long word2) -{ - unsigned long *gdt_pent, pfn = pa >> PAGE_SHIFT, d[2]; - struct pfn_info *page; - struct exec_domain *ed; - long ret = -EINVAL; - - d[0] = word1; - d[1] = word2; - - LOCK_BIGLOCK(current->domain); - - if ( (pa & 7) || (pfn >= max_page) || !check_descriptor(d) ) { - UNLOCK_BIGLOCK(current->domain); - return -EINVAL; - } - - page = &frame_table[pfn]; - if ( unlikely(!get_page(page, current->domain)) ) { - UNLOCK_BIGLOCK(current->domain); - return -EINVAL; - } - - /* Check if the given frame is in use in an unsafe context. */ - switch ( page->u.inuse.type_info & PGT_type_mask ) - { - case PGT_gdt_page: - /* Disallow updates of Xen-reserved descriptors in the current GDT. */ - for_each_exec_domain(current->domain, ed) { - if ( (l1_pgentry_to_pagenr(ed->arch.perdomain_ptes[0]) == pfn) && - (((pa&(PAGE_SIZE-1))>>3) >= FIRST_RESERVED_GDT_ENTRY) && - (((pa&(PAGE_SIZE-1))>>3) <= LAST_RESERVED_GDT_ENTRY) ) - goto out; - } - if ( unlikely(!get_page_type(page, PGT_gdt_page)) ) - goto out; - break; - case PGT_ldt_page: - if ( unlikely(!get_page_type(page, PGT_ldt_page)) ) - goto out; - break; - default: - if ( unlikely(!get_page_type(page, PGT_writable_page)) ) - goto out; - break; - } - - /* All is good so make the update. */ - gdt_pent = map_domain_mem(pa); - memcpy(gdt_pent, d, 8); - unmap_domain_mem(gdt_pent); - - put_page_type(page); - - ret = 0; /* success */ - - out: - put_page(page); - - UNLOCK_BIGLOCK(current->domain); - - return ret; -} - #ifdef MEMORY_GUARD void *memguard_init(void *heap_start) diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S index 153ae6ad6f..132d8adad3 100644 --- a/xen/arch/x86/x86_64/entry.S +++ b/xen/arch/x86/x86_64/entry.S @@ -28,8 +28,8 @@ ENTRY(hypercall) SAVE_ALL movq %r10,%rcx andq $(NR_hypercalls-1),%rax - leaq SYMBOL_NAME(hypercall_table)(%rip),%rcx - callq *(%rcx,%rax,8) + leaq SYMBOL_NAME(hypercall_table)(%rip),%rbx + callq *(%rbx,%rax,8) RESTORE_ALL addq $8,%rsp popq %rcx @@ -147,7 +147,7 @@ ENTRY(nmi) SAVE_ALL inb $0x61,%al movl %eax,%esi # reason - movl %esp,%edi # regs + movq %rsp,%rdi # regs call SYMBOL_NAME(do_nmi) jmp restore_all_xen diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c index bf6927f019..08f9e82980 100644 --- a/xen/arch/x86/x86_64/mm.c +++ b/xen/arch/x86/x86_64/mm.c @@ -240,99 +240,38 @@ long do_stack_switch(unsigned long ss, unsigned long esp) /* Returns TRUE if given descriptor is valid for GDT or LDT. */ -int check_descriptor(unsigned long *d) +int check_descriptor(struct desc_struct *d) { - unsigned long base, limit, a = d[0], b = d[1]; + u32 a = d->a, b = d->b; /* A not-present descriptor will always fault, so is safe. */ if ( !(b & _SEGMENT_P) ) goto good; - /* - * We don't allow a DPL of zero. There is no legitimate reason for - * specifying DPL==0, and it gets rather dangerous if we also accept call - * gates (consider a call gate pointing at another guestos descriptor with - * DPL 0 -- this would get the OS ring-0 privileges). - */ - if ( (b & _SEGMENT_DPL) == 0 ) + /* The guest can only safely be executed in ring 3. */ + if ( (b & _SEGMENT_DPL) != 3 ) goto bad; - if ( !(b & _SEGMENT_S) ) - { - /* - * System segment: - * 1. Don't allow interrupt or trap gates as they belong in the IDT. - * 2. Don't allow TSS descriptors or task gates as we don't - * virtualise x86 tasks. - * 3. Don't allow LDT descriptors because they're unnecessary and - * I'm uneasy about allowing an LDT page to contain LDT - * descriptors. In any case, Xen automatically creates the - * required descriptor when reloading the LDT register. - * 4. We allow call gates but they must not jump to a private segment. - */ - - /* Disallow everything but call gates. */ - if ( (b & _SEGMENT_TYPE) != 0xc00 ) - goto bad; - -#if 0 - /* Can't allow far jump to a Xen-private segment. */ - if ( !VALID_CODESEL(a>>16) ) - goto bad; -#endif + /* Any code or data segment is okay. No base/limit checking. */ + if ( (b & _SEGMENT_S) ) + goto good; - /* Reserved bits must be zero. */ - if ( (b & 0xe0) != 0 ) - goto bad; - - /* No base/limit check is needed for a call gate. */ + /* Invalid type 0 is harmless. It is used for 2nd half of a call gate. */ + if ( (b & _SEGMENT_TYPE) == 0x000 ) goto good; - } - - /* Check that base is at least a page away from Xen-private area. */ - base = (b&(0xff<<24)) | ((b&0xff)<<16) | (a>>16); - if ( base >= (PAGE_OFFSET - PAGE_SIZE) ) - goto bad; - /* Check and truncate the limit if necessary. */ - limit = (b&0xf0000) | (a&0xffff); - limit++; /* We add one because limit is inclusive. */ - if ( (b & _SEGMENT_G) ) - limit <<= 12; + /* Everything but a call gate is discarded here. */ + if ( (b & _SEGMENT_TYPE) != 0xc00 ) + goto bad; - if ( (b & (_SEGMENT_CODE | _SEGMENT_EC)) == _SEGMENT_EC ) - { - /* - * Grows-down limit check. - * NB. limit == 0xFFFFF provides no access (if G=1). - * limit == 0x00000 provides 4GB-4kB access (if G=1). - */ - if ( (base + limit) > base ) - { - limit = -(base & PAGE_MASK); - goto truncate; - } - } - else - { - /* - * Grows-up limit check. - * NB. limit == 0xFFFFF provides 4GB access (if G=1). - * limit == 0x00000 provides 4kB access (if G=1). - */ - if ( ((base + limit) <= base) || - ((base + limit) > PAGE_OFFSET) ) - { - limit = PAGE_OFFSET - base; - truncate: - if ( !(b & _SEGMENT_G) ) - goto bad; /* too dangerous; too hard to work out... */ - limit = (limit >> 12) - 1; - d[0] &= ~0x0ffff; d[0] |= limit & 0x0ffff; - d[1] &= ~0xf0000; d[1] |= limit & 0xf0000; - } - } + /* Can't allow far jump to a Xen-private segment. */ + if ( !VALID_CODESEL(a>>16) ) + goto bad; + /* Reserved bits must be zero. */ + if ( (b & 0xe0) != 0 ) + goto bad; + good: return 1; bad: @@ -340,159 +279,6 @@ int check_descriptor(unsigned long *d) } -void destroy_gdt(struct exec_domain *ed) -{ - int i; - unsigned long pfn; - - for ( i = 0; i < 16; i++ ) - { - if ( (pfn = l1_pgentry_to_pagenr(ed->arch.perdomain_ptes[i])) != 0 ) - put_page_and_type(&frame_table[pfn]); - ed->arch.perdomain_ptes[i] = mk_l1_pgentry(0); - } -} - - -long set_gdt(struct exec_domain *ed, - unsigned long *frames, - unsigned int entries) -{ - struct domain *d = ed->domain; - /* NB. There are 512 8-byte entries per GDT page. */ - int i = 0, nr_pages = (entries + 511) / 512; - struct desc_struct *vgdt; - unsigned long pfn; - - /* Check the first page in the new GDT. */ - if ( (pfn = frames[0]) >= max_page ) - goto fail; - - /* The first page is special because Xen owns a range of entries in it. */ - if ( !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) - { - /* GDT checks failed: try zapping the Xen reserved entries. */ - if ( !get_page_and_type(&frame_table[pfn], d, PGT_writable_page) ) - goto fail; - vgdt = map_domain_mem(pfn << PAGE_SHIFT); - memset(vgdt + FIRST_RESERVED_GDT_ENTRY, 0, - NR_RESERVED_GDT_ENTRIES*8); - unmap_domain_mem(vgdt); - put_page_and_type(&frame_table[pfn]); - - /* Okay, we zapped the entries. Now try the GDT checks again. */ - if ( !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) - goto fail; - } - - /* Check the remaining pages in the new GDT. */ - for ( i = 1; i < nr_pages; i++ ) - if ( ((pfn = frames[i]) >= max_page) || - !get_page_and_type(&frame_table[pfn], d, PGT_gdt_page) ) - goto fail; - - /* Copy reserved GDT entries to the new GDT. */ - vgdt = map_domain_mem(frames[0] << PAGE_SHIFT); - memcpy(vgdt + FIRST_RESERVED_GDT_ENTRY, - gdt_table + FIRST_RESERVED_GDT_ENTRY, - NR_RESERVED_GDT_ENTRIES*8); - unmap_domain_mem(vgdt); - - /* Tear down the old GDT. */ - destroy_gdt(ed); - - /* Install the new GDT. */ - for ( i = 0; i < nr_pages; i++ ) - ed->arch.perdomain_ptes[i] = - mk_l1_pgentry((frames[i] << PAGE_SHIFT) | __PAGE_HYPERVISOR); - - SET_GDT_ADDRESS(ed, GDT_VIRT_START(ed)); - SET_GDT_ENTRIES(ed, entries); - - return 0; - - fail: - while ( i-- > 0 ) - put_page_and_type(&frame_table[frames[i]]); - return -EINVAL; -} - - -long do_set_gdt(unsigned long *frame_list, unsigned int entries) -{ - int nr_pages = (entries + 511) / 512; - unsigned long frames[16]; - long ret; - - if ( (entries <= LAST_RESERVED_GDT_ENTRY) || (entries > 8192) ) - return -EINVAL; - - if ( copy_from_user(frames, frame_list, nr_pages * sizeof(unsigned long)) ) - return -EFAULT; - - if ( (ret = set_gdt(current, frames, entries)) == 0 ) - { - local_flush_tlb(); - __asm__ __volatile__ ("lgdt %0" : "=m" (*current->arch.gdt)); - } - - return ret; -} - - -long do_update_descriptor( - unsigned long pa, unsigned long word1, unsigned long word2) -{ - unsigned long *gdt_pent, pfn = pa >> PAGE_SHIFT, d[2]; - struct pfn_info *page; - long ret = -EINVAL; - - d[0] = word1; - d[1] = word2; - - if ( (pa & 7) || (pfn >= max_page) || !check_descriptor(d) ) - return -EINVAL; - - page = &frame_table[pfn]; - if ( unlikely(!get_page(page, current->domain)) ) - return -EINVAL; - - /* Check if the given frame is in use in an unsafe context. */ - switch ( page->u.inuse.type_info & PGT_type_mask ) - { - case PGT_gdt_page: - /* Disallow updates of Xen-reserved descriptors in the current GDT. */ - if ( (l1_pgentry_to_pagenr(current->arch.perdomain_ptes[0]) == pfn) && - (((pa&(PAGE_SIZE-1))>>3) >= FIRST_RESERVED_GDT_ENTRY) && - (((pa&(PAGE_SIZE-1))>>3) <= LAST_RESERVED_GDT_ENTRY) ) - goto out; - if ( unlikely(!get_page_type(page, PGT_gdt_page)) ) - goto out; - break; - case PGT_ldt_page: - if ( unlikely(!get_page_type(page, PGT_ldt_page)) ) - goto out; - break; - default: - if ( unlikely(!get_page_type(page, PGT_writable_page)) ) - goto out; - break; - } - - /* All is good so make the update. */ - gdt_pent = map_domain_mem(pa); - memcpy(gdt_pent, d, 8); - unmap_domain_mem(gdt_pent); - - put_page_type(page); - - ret = 0; /* success */ - - out: - put_page(page); - return ret; -} - #ifdef MEMORY_GUARD #define ALLOC_PT(_level) \ diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h index 34934faad8..81e60a05fe 100644 --- a/xen/include/asm-x86/mm.h +++ b/xen/include/asm-x86/mm.h @@ -219,7 +219,7 @@ static inline int get_page_and_type(struct pfn_info *page, ASSERT(((_p)->count_info & PGC_count_mask) != 0); \ ASSERT(page_get_owner(_p) == (_d)) -int check_descriptor(unsigned long *d); +int check_descriptor(struct desc_struct *d); /* * Use currently-executing domain's pagetables on the specified CPUs. diff --git a/xen/include/asm-x86/multicall.h b/xen/include/asm-x86/multicall.h index d03ac9ffb1..f4bac0a150 100644 --- a/xen/include/asm-x86/multicall.h +++ b/xen/include/asm-x86/multicall.h @@ -9,7 +9,23 @@ #ifdef __x86_64__ -#define do_multicall_call(_call) BUG() +#define do_multicall_call(_call) \ + do { \ + __asm__ __volatile__ ( \ + "movq "STR(MULTICALL_op)"(%0),%%rax; " \ + "andq $("STR(NR_hypercalls)"-1),%%rax; " \ + "leaq "STR(hypercall_table)"(%%rip),%%rdi; "\ + "leaq (%%rdi,%%rax,8),%%rax; " \ + "movq "STR(MULTICALL_arg0)"(%0),%%rdi; " \ + "movq "STR(MULTICALL_arg1)"(%0),%%rsi; " \ + "movq "STR(MULTICALL_arg2)"(%0),%%rdx; " \ + "movq "STR(MULTICALL_arg3)"(%0),%%rcx; " \ + "movq "STR(MULTICALL_arg4)"(%0),%%r8; " \ + "callq *(%%rax); " \ + "movq %%rax,"STR(MULTICALL_result)"(%0); " \ + : : "b" (_call) \ + : "rax", "rdi", "rsi", "rdx", "rcx", "r8" ); \ + } while ( 0 ) #else -- 2.30.2